; ceci n'est pas un cube

SCREEN_BASE_PIXELS		equ 49152
SCREEN_SIZE_PIXELS		equ	2048*3
SCREEN_BASE_ATTR		equ SCREEN_BASE_PIXELS+SCREEN_SIZE_PIXELS
SCREEN_SIZE_ATTR		equ	32*24

NUM_POINTS				equ	8
POINT_SCALE				equ	32

ROM_CL_CHAN				equ	0xd94
ROM_PR_STRING			equ	0x203c
ROM_PIXEL_ADD			equ	0x22aa
PATCHED_ROM_PLOT		equ	0x22dc+32768
PATCHED_ROM_DRAW_LINE	equ	0x24b7+32768
ROM_STACK_A				equ	0x2d28
ROM_STK_TO_BC			equ	0x2307
ROM_STK_TO_A			equ	0x2314

CALC_JUMP_TRUE			equ	0x00
CALC_EXCHANGE			equ	0x01
CALC_DELETE				equ	0x02
CALC_SUBTRACTION		equ	0x03
CALC_MULTIPLY			equ	0x04
CALC_DIVISION			equ	0x05
CALC_ADDITION			equ	0x0f
CALC_NEGATE				equ	0x1b
CALC_SIN				equ	0x1f
CALC_COS				equ	0x20
CALC_DUPLICATE			equ	0x31
CALC_END_CALC			equ	0x38
CALC_STK_ZERO			equ	0xa0
CALC_STK_ONE			equ	0xa1
CALC_STK_HALF			equ	0xa2
CALC_STK_PI2			equ	0xa3
CALC_STK_TEN			equ	0xa4
CALC_ST_MEM_0			equ	0xc0
CALC_ST_MEM_1			equ	0xc1
CALC_ST_MEM_2			equ	0xc2
CALC_ST_MEM_3			equ	0xc3
CALC_ST_MEM_4			equ	0xc4
CALC_ST_MEM_5			equ	0xc5
CALC_GET_MEM_0			equ	0xe0
CALC_GET_MEM_1			equ	0xe1
CALC_GET_MEM_2			equ	0xe2
CALC_GET_MEM_3			equ	0xe3
CALC_GET_MEM_4			equ	0xe4
CALC_GET_MEM_5			equ	0xe5


	org		32768
start
	; copy rom routines
	ld		hl,0+1024
	ld		de,32768+1024
;	ld		bc,16384-1024
	ldir

	; patch pixel-add routine to get shadow screen address instead
	ld		hl,0x22ca+32768
	ld		(hl),0xcb
	inc		hl
	ld		(hl),0xfc
	inc		hl
	ld		(hl),0xc9

	; patch plot routine to use our plot-sub
	ld		a,h
	ld		hl,0x22e0+32768 + 1
	ld		(hl),a

	; patch jump inside plot routine to use our patched pixel-add routine
;	ld		hl,0x22ea+32768 + 1
	ld		l,LOW(0x22ea+32768 + 1)
	ld		(hl),a

	; patch draw routine to use our patched plot routine
	ld		hl,0x24ef+32768 + 1
	ld		(hl),a

	; hack out the jump to the attribute setting in the draw routine with a ret
	; actually.. there's not much of a speed-up from this..
;	ld		hl,0x2304+32768
;	ld		(hl),0xc9


	; main loop starts here..
	ld		a,00010111b
.mainloop
	; bank switch
	xor		00001010b
;	ld		bc,$7ffd
;	put		(c),a
	out		($fd),a
	push	af

	; screen clear
	ld		hl,SCREEN_BASE_PIXELS
	ld		de,SCREEN_BASE_PIXELS+1
	ld		(hl),l
	ld		bc,SCREEN_SIZE_PIXELS
	ldir
	ld		(hl),111000b
;	ld		bc,SCREEN_SIZE_ATTR-1
	ld		b,HIGH(SCREEN_SIZE_ATTR)
	ldir

	; reset print pos
	call	ROM_CL_CHAN

	; text logo
	ld		bc,5
	ld		de,.textdata
	call	ROM_PR_STRING

	; read and increase angle
.angle	
	ex		de,hl				; use the next byte in de as angle..
	ld		a,(hl)
	inc		(hl)

	; get sin(angle) into mem3 and cos(angle) into mem4
	call	ROM_STACK_A			; stack angle
	rst		0x28
	db		CALC_STK_TEN		; angle / 10
	db		CALC_DIVISION
	db		CALC_DUPLICATE
	db		CALC_COS
	db		CALC_ST_MEM_4		; mem4 = cos(a)
	db		CALC_DELETE
	db		CALC_SIN
	db		CALC_ST_MEM_3		; mem3 = sin(a)
	db		CALC_DELETE
	db		CALC_END_CALC

	call	.draw
	call	.draw	

	pop		af
	jr		.mainloop


	; draw one half of a cube
.draw
	ld		hl,.flip
	ld		a,CALC_STK_ONE XOR CALC_STK_ZERO
	xor		(hl)
	ld		(hl),a

	; frame setup
;	ld		hl,.pointdata
	ld		l,LOW(.pointdata)

	call	.calcPoint
	push	hl
	call	PATCHED_ROM_PLOT
	pop		hl

	ld		b,NUM_POINTS-1
.drawloop
	push	bc
	call	.calcPoint
	push	hl

	; calc line vector and leave on stack
	ld		a,(23678)
	call	ROM_STACK_A
	rst		0x28
	db		CALC_SUBTRACTION
	db		CALC_EXCHANGE
	db		CALC_END_CALC
	ld		a,(23677)
	call	ROM_STACK_A
	rst		0x28
	db		CALC_SUBTRACTION
	db		CALC_EXCHANGE
	db		CALC_END_CALC

	call	PATCHED_ROM_DRAW_LINE
	pop		hl
	pop		bc
	djnz	.drawloop

	ret


	; calc point
	; 	simple rotation around z:
	;		x = cos(a) * x + sin(a) * z
	;		y = y
	;		z = cos(a) * z - sin(a) * x 
	; 	..and projection:
	;		x = x / z + xoff
	;		y = y / z + yoff
.calcPoint
	ld		a,(hl)				; get next set of co-ord bits
	inc		hl
	push	hl
	ex		af,af'				; swap out co-ord bits
	ld		a,CALC_ST_MEM_0
	call	.get_coord			; get x into mem 0
	call	.get_coord			; get y into mem 1
	call	.get_coord			; get z into mem 2

	ld		a,192/2-17
	call	ROM_STACK_A
	ld		a,256/2
	call	ROM_STACK_A

	rst	0x28
	;	x = cos(a) * x + sin(a) * z
	db		CALC_GET_MEM_4
	db		CALC_GET_MEM_0
	db		CALC_MULTIPLY
	db		CALC_GET_MEM_3
	db		CALC_GET_MEM_2
	db		CALC_MULTIPLY
	db		CALC_ADDITION
	db		CALC_ST_MEM_5		; mem5 = rotated x
	db		CALC_DELETE
	;	z = ( cos(a) * z - sin(a) * x ) - 100	the -100 is translation
	db		CALC_GET_MEM_4
	db		CALC_GET_MEM_2
	db		CALC_MULTIPLY
	db		CALC_GET_MEM_3
	db		CALC_GET_MEM_0
	db		CALC_MULTIPLY
	db		CALC_SUBTRACTION
	db		CALC_STK_TEN
	db		CALC_STK_TEN
	db		CALC_MULTIPLY
	db		CALC_ST_MEM_0		; store 100 in mem0 for later use
	db		CALC_SUBTRACTION
	db		CALC_ST_MEM_2		; mem2 = rotated z
	db		CALC_DELETE
	;	y = ( y / z ) * 100 + yoff	the 100 is scale
	db		CALC_GET_MEM_1
	db		CALC_GET_MEM_2
	db		CALC_DIVISION
	db		CALC_GET_MEM_0
	db		CALC_MULTIPLY
	db		CALC_ADDITION
	db		CALC_EXCHANGE
	;	x = ( x / z ) * 100 + xoff	the 100 is scale
	db		CALC_GET_MEM_5
	db		CALC_GET_MEM_2
	db		CALC_DIVISION
	db		CALC_GET_MEM_0
	db		CALC_MULTIPLY
	db		CALC_ADDITION

	db		CALC_END_CALC		; calc stack now contains x, y

	pop		hl
	ret


	; get a coord from hl onto the calculator stack
	; a = calc mem store instruction
.get_coord
	ld		(.gc_mem),a
	inc		a
	push	af
	ex		af,af'				; swap in co-ord bits
	rrca						; get next co-ord bit
	ld		c,128-POINT_SCALE	; bit not set means -ve
	jr		c,.st
	ld		c,128+POINT_SCALE	; bit set means +ve
.st	ex		af,af'				; swap out co-ord bits
	call	ROM_STACK_A+1		; stack co-ord
	ld		a,128				; stack 128
	call	ROM_STACK_A
	rst		0x28
	db		CALC_SUBTRACTION
.flip
	db		CALC_STK_ONE
	db		CALC_JUMP_TRUE,2
	db		CALC_NEGATE
.gc_mem
	db		CALC_ST_MEM_0
	db		CALC_DELETE
	db		CALC_END_CALC
	pop		af
	ret


	; point data ( x, y, z )
	; bit set means a positive point, not set mean negative
.pointdata
	db		%000
	db		%010
	db		%011
	db		%001
	db		%000
	db		%100
	db		%101
;	db		%001				; cunningly the lower nibble of '!' is %001 ;)


.textdata
	db		"!cube"

end start
